package com.jeomo.mem.starter.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jeomo.base.starter.service.impl.BaseServiceImpl;
import com.jeomo.common.core.util.StringUtils;
import com.jeomo.common.util.DoubleUtil;
import com.jeomo.masterdata.starter.entity.Goods;
import com.jeomo.masterdata.starter.entity.Merchant;
import com.jeomo.masterdata.starter.enums.UseScopeEnum;
import com.jeomo.masterdata.starter.mapper.GoodsMapper;
import com.jeomo.masterdata.starter.mapper.MerchantMapper;
import com.jeomo.masterdata.starter.service.ILevelService;
import com.jeomo.mem.starter.dto.MemberPointRuleDto;
import com.jeomo.mem.starter.entity.MemberPointsRule;
import com.jeomo.mem.starter.entity.MemberPointsRuleExe;
import com.jeomo.mem.starter.entity.MemberPointsRuleList;
import com.jeomo.mem.starter.mapper.MemberPointsRuleExeMapper;
import com.jeomo.mem.starter.mapper.MemberPointsRuleListMapper;
import com.jeomo.mem.starter.mapper.MemberPointsRuleMapper;
import com.jeomo.mem.starter.service.IMemberPointsService;
import com.jeomo.mem.starter.util.MemberUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;

/**
 * 会员积分规则服务
 * @author:Jackpot
 * @create 2020-09-09 15:32
 * @description:
 */
@Service
@Transactional
public class MemberPointsServiceImpl extends BaseServiceImpl<MemberPointsRuleListMapper, MemberPointsRuleList>
        implements IMemberPointsService {

    private Logger logger = LoggerFactory.getLogger(MemberPointsServiceImpl.class);

    private final String RULE_CODE = "rule_code";

    @Resource
    private MemberPointsRuleMapper memberPointsRuleMapper;
    @Resource
    private MemberPointsRuleListMapper memberPointsRuleListMapper;
    @Resource
    private MemberPointsRuleExeMapper memberPointsRuleExeMapper;
    @Resource
    private GoodsMapper goodsMapper;
    @Resource
    private MerchantMapper merchantMapper;
    @Resource
    private ILevelService levelService;

    @Override
    public int saveRule(MemberPointsRule memberPointsRule, List<MemberPointsRuleList> memberPointsRuleList) {

        int result = 0;
        String code = memberPointsRule.getCode();
        if (code!=null){
            MemberPointsRule memberPointsRule1 = memberPointsRuleMapper.selectById(code);
            if (memberPointsRule1 !=null){
                //修改规则
                result = saveRule(memberPointsRule,memberPointsRuleList,false);
            }else {
                //数据已被删除
                return -2;
            }
        }else{
            //新增规则
            result = saveRule(memberPointsRule,memberPointsRuleList,true);
        }
        return result;
    }


    @Override
    public int saveRule(MemberPointsRule memberPointsRule, List<MemberPointsRuleList> memberPointsRuleList,boolean addOrUpdate) {

        memberPointsRule.setUseLevels(StringUtils.arrayToStr(memberPointsRule.getLevelCodes()));

        String ruleIdIfConflict = verifyBeforeSave(memberPointsRule, memberPointsRuleList);
        if(ruleIdIfConflict!=null){
            return -3;
        }
        int masterResult = 0;
        String code = memberPointsRule.getCode();
        if(addOrUpdate==true){
            //新增规则
            masterResult = memberPointsRuleMapper.insert(memberPointsRule);
            code = memberPointsRule.getCode();
        }else{
            //修改规则: 修改主表,删除子表原数据,新增子表数据
            masterResult = memberPointsRuleMapper.updateById(memberPointsRule);
            if(masterResult < 1) return -1; //throw new ConcurrentModificationException("数据已发生变化,请刷新后重试!");
            int delList = deleteRuleDetail(code);
        }
        int detailResult = insertRuleDetail(memberPointsRule,memberPointsRuleList);
        boolean b = saveRuleExec(memberPointsRule, memberPointsRuleList, addOrUpdate);
        return masterResult+detailResult;
    }

    /**
     * 保存积分规则执行单
     * @param memberPointsRule
     * @param memberPointsRuleList
     * @param addOrUpdate
     * @return
     */
    public boolean saveRuleExec(MemberPointsRule memberPointsRule, List<MemberPointsRuleList> memberPointsRuleList,boolean addOrUpdate) {

        String code = memberPointsRule.getCode();
        //如果是修改,先删除该规则所有的执行数据
        if(addOrUpdate==false){
            QueryWrapper<MemberPointsRuleExe> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq(RULE_CODE,code);
            memberPointsRuleExeMapper.delete(queryWrapper);
        }
        //准备执行单数据
        List<MemberPointsRuleExe> memberPointsRuleExes = new ArrayList<>();
        Integer amountFloor = memberPointsRule.getAmountFloor();
        Integer pointsFloor = memberPointsRule.getPointsFloor();

        String[] levels =  memberPointsRule.getLevelCodes();

        //如果该积分规则的会员等级配置3个,使用范围明细是100个,则把数据平铺开来, 3*100=300条数据存储到执行单上
        for(String level:levels){
            for(MemberPointsRuleList bo :memberPointsRuleList){
                String bsCode = bo.getCode();

                MemberPointsRuleExe memberPointsRuleExe = new MemberPointsRuleExe();
                memberPointsRuleExe.setRuleCode(code);
                memberPointsRuleExe.setLevelCode(level);
                memberPointsRuleExe.setBsCode(bsCode);
                memberPointsRuleExe.setAmountFloor(amountFloor);
                memberPointsRuleExe.setPointsFloor(pointsFloor);
                memberPointsRuleExes.add(memberPointsRuleExe);
            }
        }
        //新增
        int insert =0;
        int size = memberPointsRuleExes.size();
        for(MemberPointsRuleExe exe :memberPointsRuleExes){
            insert += memberPointsRuleExeMapper.insert(exe);
        }
        return insert==size;
    }

    /**
     * 保存前校验: 是否存在配置冲突的数据存在;<br/>
     * 配置积分规则: 等级+业务编码 确定唯一数据
     * @param memberPointsRule
     * @param memberPointsRuleList
     * @return 返回配置冲突的ruleId; 如果返回null 表示不冲突,校验通过;
     */
    private String verifyBeforeSave(MemberPointsRule memberPointsRule, List<MemberPointsRuleList> memberPointsRuleList){

        String[] levels =  memberPointsRule.getLevelCodes();
        String useLevels=StringUtils.arrayToStr(levels);
        List<MemberPointsRuleExe> memberPointsRuleExes = memberPointsRuleExeMapper.getPointsRuleExesByLevels(useLevels);
        HashMap<String, String> curRuleMap = new HashMap<>(64);
        for (MemberPointsRuleExe exe :memberPointsRuleExes){
            String levelCode = exe.getLevelCode();
            String bsCode = exe.getBsCode();
            String ruleCode = exe.getRuleCode();
            curRuleMap.put(levelCode+bsCode,ruleCode);
        }

        String code = memberPointsRule.getCode();

        for(String levelCode:levels){
            for(MemberPointsRuleList bo :memberPointsRuleList){
                String bsCode = bo.getCode();
                String ruleCode1 = curRuleMap.get(levelCode + bsCode);
                if(ruleCode1 !=null && !ruleCode1.equals(code)){
                    return ruleCode1;
                }
            }
        }
        return null;
    }

    @Override
    public int insertRuleDetail(MemberPointsRule memberPointsRule,List<MemberPointsRuleList> memberPointsRuleList) {
        String ruleCode = memberPointsRule.getCode();
        Integer useRange = memberPointsRule.getUseRange();
        Iterator<MemberPointsRuleList> iterator = memberPointsRuleList.iterator();
        while (iterator.hasNext()){
            MemberPointsRuleList detail = iterator.next();
            detail.setRuleCode(ruleCode);
            detail.setBsType(useRange);
        }
        return saveBatch(memberPointsRuleList) == true?1:0;
    }

    @Override
    public int deleteRule(String ruleCode) {

        int delMaster = memberPointsRuleMapper.deleteById(ruleCode);
        int delList = deleteRuleDetail(ruleCode);
        int delList2 = deleteRuleExe(ruleCode);
        return delList+delMaster+delList2;
    }

    @Override
    public int deleteRuleDetail(String ruleCode) {
        QueryWrapper<MemberPointsRuleList> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(RULE_CODE,ruleCode);
        return memberPointsRuleListMapper.delete(queryWrapper);
    }

    @Override
    public int deleteRuleExe(String ruleCode) {
        QueryWrapper<MemberPointsRuleExe> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(RULE_CODE,ruleCode);
        return memberPointsRuleExeMapper.delete(queryWrapper);
    }

    @Override
    public MemberPointRuleDto getMemberPointRule(String ruleCode) {

        MemberPointsRule memberPointsRule = memberPointsRuleMapper.selectById(ruleCode);
        List<MemberPointsRuleList> memberPointsRuleList = memberPointsRuleListMapper.getMemberPointsRuleList(ruleCode);
        MemberPointRuleDto memberPointRuleDto = new MemberPointRuleDto();
        memberPointRuleDto.setMemberPointsRule(memberPointsRule);

        Map<String, String> levelMap = levelService.queryCache();
        MemberUtil.assignName(memberPointsRule,levelMap);
        memberPointRuleDto.setMemberPointsRuleList(memberPointsRuleList);
        return memberPointRuleDto;
    }

    @Override
    public List<MemberPointsRule> getAllPointRules(){
        return memberPointsRuleMapper.selectList(null);
    }

    @Override
    public int getPoints(String levelCode, String bsCode, double amount, UseScopeEnum useScopeEnum){

        MemberPointsRuleExe ruleExe = null;
        if (useScopeEnum==UseScopeEnum.GOODS){
            ruleExe = getPointsRuleByGoods(levelCode, bsCode);
        }else if (useScopeEnum==UseScopeEnum.MERCHANT){
            ruleExe = getPointsRuleByMerchant(levelCode, bsCode);
        }
        return getPoints(ruleExe,amount);
    }

    @Override
    public int getPoints(MemberPointsRuleExe memberPointsRuleExe, double amount){
        int points = 0;
        if (memberPointsRuleExe==null) return points;
        Integer amountFloor = memberPointsRuleExe.getAmountFloor();
        Integer pointsFloor = memberPointsRuleExe.getPointsFloor();

        double num = DoubleUtil.div(amount, amountFloor, 2);
        points = ((int)Math.floor(num)) * pointsFloor;
        return points;
    }

    /**
     * 获取积分规则<br/>
     * 查询优先级: 商品 > 品类 > 商户 > 业态
     * @param levelCode 会员等级
     * @param goodsCode 商品编码
     * @return
     */
    public MemberPointsRuleExe getPointsRuleByGoods(String levelCode, String goodsCode){

        HashMap<String, MemberPointsRuleExe> curRuleMap = queryRuleCache(levelCode);
        MemberPointsRuleExe rule = null;
        //商品对应的积分规则
        rule = curRuleMap.get(goodsCode);
        if (rule!=null) return rule;
        Goods goods = goodsMapper.selectById(goodsCode);
        if (goods == null) return null;
        //查询品类的规则
        String goodsTypeCode = goods.getGoodsTypeCode();
        rule = curRuleMap.get(goodsTypeCode);
        if (rule!=null) return rule;
        //查询商户的规则
        String merchantCode = goods.getMerchantCode();
        rule = curRuleMap.get(merchantCode);
        if (rule!=null) return rule;

        Merchant merchant = merchantMapper.selectById(merchantCode);
        if (merchant == null) return null;
        //查询业态的规则
        return curRuleMap.get(merchant.getTobCode());
    }

    /**
     * 根据商户查询积分规则
     * 先查询商户,再查询业态
     * @param levelCode
     * @param merchantCode
     * @return
     */
    public MemberPointsRuleExe getPointsRuleByMerchant(String levelCode,String merchantCode){

        HashMap<String, MemberPointsRuleExe> curRuleMap = queryRuleCache(levelCode);
        MemberPointsRuleExe rule = null;
        //查询商户的规则
        rule = curRuleMap.get(merchantCode);
        if (rule!=null) return rule;
        Merchant merchant = merchantMapper.selectById(merchantCode);
        if (merchant == null) return null;
        //查询业态的规则
        return curRuleMap.get(merchant.getTobCode());
    }

    public HashMap<String, MemberPointsRuleExe> queryRuleCache(String levelCode){
        List<MemberPointsRuleExe> memberPointsRuleExes = memberPointsRuleExeMapper.getPointsRuleExesByLevel(levelCode);
        HashMap<String, MemberPointsRuleExe> curRuleMap = new HashMap<>(64);
        for (MemberPointsRuleExe exe :memberPointsRuleExes){
            String bsCode = exe.getBsCode();
            curRuleMap.put(bsCode,exe);
        }
        return curRuleMap;
    }
}
